package org.kernely.security;

import java.util.HashSet;
import java.util.Set;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.InvalidSessionException;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.kernely.security.dao.GroupDAO;
import org.kernely.security.dao.PermissionDAO;
import org.kernely.security.dao.RoleDAO;
import org.kernely.security.dao.UserDAO;
import org.kernely.security.persistence.Group;
import org.kernely.security.persistence.Permission;
import org.kernely.security.persistence.Role;
import org.kernely.security.persistence.User;
import org.kernely.security.service.KernelySecurityService;

public class KernelySecurityServiceImpl implements KernelySecurityService {

	// The attribute name of the current authenticated user
	private final String SESSION_ATTRIBUTE_USER = "user";

	/**
	 * @see KernelySecurityService#getCurrentUser()
	 */
	public final User getCurrentUser() {
		try {
			Session session = SecurityUtils.getSubject().getSession();
			int currentUserId = (Integer) session.getAttribute(SESSION_ATTRIBUTE_USER);
			UserDAO udao = UserDAO.getInstance();
			return udao.getUser(currentUserId);
		} catch(InvalidSessionException ise){
			// Session has expired
			return null;
		}
	}

	/**
	 * @see KernelySecurityService#setCurrentUser()
	 */
	public final void setCurrentUser(User user) {
		Session session = SecurityUtils.getSubject().getSession();
		session.setAttribute(SESSION_ATTRIBUTE_USER, user.getId());
	}

	/**
	 * @see KernelySecurityService#currentUserHasOneOf(String ... rolesList)
	 */
	public final boolean currentUserHasOneOf(String ... rolesList){
		Subject subject= SecurityUtils.getSubject();

		for(String s : rolesList){
			if (subject.hasRole(s)){
				return true;
			}
		}
		return false;
	}

	/**
	 * @see KernelySecurityService#currentUserHasOneOf(Set<String> rolesSet)
	 */
	public final boolean currentUserHasOneOf(Set<String> rolesSet){
		Subject subject= SecurityUtils.getSubject();

		for(String s : rolesSet){
			if (subject.hasRole(s)){
				return true;
			}
		}
		return false;
	}



	/**
	 * @see org.kernely.security.service.KernelySecurityService#grantAuthorization(org.kernely.security.persistence.User, java.lang.String)
	 */
	public final void grantAuthorization(User user, String authorizationString){
		PermissionDAO pdao = PermissionDAO.getInstance();
		Permission permission = pdao.findPermissionByName(authorizationString);
		// If the permission doesn't exist in database, we create it
		if(permission == null){
			permission = new Permission();
			permission.setName(authorizationString);
			pdao.savePermission(permission);
		}

		// Association user / permission
		UserDAO udao = UserDAO.getInstance();
		Set<Permission> userPermissions = user.getPermissions();
		if (userPermissions == null){
			userPermissions = new HashSet<Permission>();
		}
		userPermissions.add(permission);
		user.setPermissions(userPermissions);
		udao.update(user);
	}

	/**
	 * @see org.kernely.security.service.KernelySecurityService#ungrantAuthorization(org.kernely.security.persistence.User, java.lang.String)
	 */
	public final void ungrantAuthorization(User user, String authorizationString){
		PermissionDAO pdao = PermissionDAO.getInstance();
		Permission permission = pdao.findPermissionByName(authorizationString);
		if(permission != null){
			// Remove the link user / permission
			UserDAO udao = UserDAO.getInstance();
			Set<Permission> userPermissions = user.getPermissions();
			if (userPermissions != null){
				userPermissions.remove(permission);
				user.setPermissions(userPermissions);
				udao.update(user);
			}
		}
	}

	/**
	 * @see org.kernely.security.service.KernelySecurityService#grantAuthorization(org.kernely.security.persistence.Group, java.lang.String)
	 */
	public final void grantAuthorization(Group group, String authorizationString){
		PermissionDAO pdao = PermissionDAO.getInstance();
		Permission permission = pdao.findPermissionByName(authorizationString);
		// If the permission doesn't exist in database, we create it
		if(permission == null){
			permission = new Permission();
			permission.setName(authorizationString);
			pdao.savePermission(permission);
		}

		// Association group / permission
		GroupDAO gdao = GroupDAO.getInstance();
		Set<Permission> groupPermissions = group.getPermissions();
		if (groupPermissions == null){
			groupPermissions = new HashSet<Permission>();
		}
		groupPermissions.add(permission);
		group.setPermissions(groupPermissions);
		gdao.updateGroup(group);
	}

	/**
	 * @see org.kernely.security.service.KernelySecurityService#ungrantAuthorization(org.kernely.security.persistence.Group, java.lang.String)
	 */
	public final void ungrantAuthorization(Group group, String authorizationString){
		PermissionDAO pdao = PermissionDAO.getInstance();
		Permission permission = pdao.findPermissionByName(authorizationString);
		if(permission != null){
			// Remove the link user / permission
			GroupDAO gdao = GroupDAO.getInstance();
			Set<Permission> groupPermissions = group.getPermissions();
			if (groupPermissions != null){
				groupPermissions.remove(permission);
				group.setPermissions(groupPermissions);
				gdao.updateGroup(group);
			}
		}
	}

	/**
	 * @see org.kernely.security.service.KernelySecurityService#currentUserHasPermission(java.lang.String)
	 */
	public final boolean currentUserHasPermission(String permission) {
		Subject subject= SecurityUtils.getSubject();
		return subject.isPermitted(permission);
	}

	
	/**
	 * @see org.kernely.security.service.KernelySecurityService#registerNewRole(java.lang.String)
	 */
	public void registerNewRole(String roleName) {
		boolean exists = RoleDAO.getInstance().findRoleByName(roleName) != null;
		if (!exists){
			Role newRole = new Role();
			newRole.setName(roleName);
			RoleDAO.getInstance().saveRole(newRole);
		}
	}
	
	/**
	 * @see org.kernely.security.service.KernelySecurityService#registerNewRoles(java.util.Set)
	 */
	public void registerNewRoles(Set<String> rolesNames) {
		for (String r : rolesNames){
			this.registerNewRole(r);
		}
	}
}
